
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <iostream>
#include <string>

#include <v8.h>

using namespace std;
using namespace v8;

// DIRTY
static Handle<Value> LogCallback(const Arguments& args) {                                                                    
  if (args.Length() < 1) return v8::Undefined();
  HandleScope scope;
  Handle<Value> arg = args[0];
  String::Utf8Value value(arg);
  cerr << "LOG: " << *value << "\n";
  return v8::Undefined();
}


// DIRTY
// Reads a file into a v8 string.
v8::Handle<v8::String> ReadFile(const char* name) {
  FILE* file = fopen(name, "rb");
  if (file == NULL) return v8::Handle<v8::String>();

  fseek(file, 0, SEEK_END);
  int size = ftell(file);
  rewind(file);

  char* chars = new char[size + 1];
  chars[size] = '\0';
  for (int i = 0; i < size;) {
    int read = fread(&chars[i], 1, size - i, file);
    i += read;
  }
  fclose(file);                                                                                                              
  v8::Handle<v8::String> result = v8::String::New(chars, size);
  delete[] chars;
  return result;
}

// DIRTY
// Executes a string within the current v8 context.
bool ExecuteString(v8::Handle<v8::String> source,
                   v8::Handle<v8::Value> name) {
  v8::HandleScope handle_scope;
  v8::TryCatch try_catch;
  v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
  if (script.IsEmpty()) {
    return false;
  } else {
    v8::Handle<v8::Value> result = script->Run();
    if (result.IsEmpty()) {
      return false;
    } else {
      return true;
    }
  }
}

const std::string input[] = { "abc", "def", "xyz", "" };

Handle<Function> FindFunc(const string &name) { 
  Handle<String> func_name = String::New(name.c_str());
  Handle<Value> func_val = Context::GetEntered()->Global()->Get(func_name);
  Handle<Function> func;
  if (func_val->IsFunction()) {
    func = Handle<Function>::Cast(func_val);
  }
  return func;
}

int RunMain(int argc, char* argv[]) {
  v8::V8::SetFlagsFromCommandLine(&argc, argv, true);

  if (argc < 2) {
    std::cerr << "Error: script file required.\n";
    return 1;
  }

  HandleScope handle_scope;

  Handle<ObjectTemplate> global = ObjectTemplate::New();
  global->Set(String::New("log"), FunctionTemplate::New(LogCallback));

  Handle<Context> context = Context::New(NULL, global);
  Context::Scope context_scope(context);

  Handle<String> source = ReadFile(argv[1]);

  if(source.IsEmpty()) {
    std::cerr << "Error: invalid JavaScript file" << endl;
    return 1;
  }

  if (!ExecuteString(source, v8::String::New("script"))) {
    std::cerr << "Error: script file failed to execute.\n";
    return 1;
  }

  for (int i = 0; "" != input[i]; ++i) {
    std::cerr << input[i] << "\n";
    Handle<Function> map_func = FindFunc("map");
    if (map_func.IsEmpty()) {
      std::cerr << "Error: map not defined in environment.\n";
    }

    TryCatch try_catch;
    const int argc = 1;
    Handle<Value> argv[argc] = { v8::String::New(input[i].c_str()) };
    Handle<Value> result = map_func->Call(context->Global(), argc, argv);
    if (result.IsEmpty()) {
      String::Utf8Value error(try_catch.Exception());
      cerr << "Script exception: " << *error << "\n";
    }

  }

  return 0;
}

int main(int argc, char* argv[]) {
  int result = RunMain(argc, argv);
  v8::V8::Dispose();
  return result;
}
